package assist.client.gwt.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import assist.domain.model.Games;
import assist.domain.model.Player;
import assist.domain.model.Team;
import assist.service.gwt.interfaces.HockeyStatsService;
import assist.service.gwt.interfaces.HockeyStatsServiceAsync;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.view.client.AsyncDataProvider;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.Range;
import com.google.web.bindery.event.shared.EventBus;

public class PlayersProvider extends AsyncDataProvider<Player> implements ValueChangeHandler<Games>, HasHandlers
{
    private final HockeyStatsServiceAsync hockeyStatsService =
        GWT.create(HockeyStatsService.class);
    private HandlerManager handlerManager;

    private EventBus eventBus; 
    private GamesProvider gamesProvider;    
    private Team team;
    private List<Player> players;
    private Map<Long, Player> playersMap;
    
    public PlayersProvider(EventBus eventBus, GamesProvider gamesProvider, Team team)
    {        
        this.eventBus = eventBus;
        this.handlerManager = new HandlerManager(this);
        //Register onGamesChange, so we can wait for fetchPlayers.
        this.gamesProvider = gamesProvider;
        this.team = team;
        if ( this.gamesProvider.getGames() != null)
        {
            fetchPlayers();
        }
        this.gamesProvider.addGamesChangeHandler(this);
    } 
    
    @Override
    protected void onRangeChanged(HasData<Player> display)
    {
        if (this.players == null || this.players.isEmpty())
        {
            display.setRowData(0, new ArrayList<Player>());
            return;
        }
        final Range range = display.getVisibleRange();
        int start = range.getStart();
        //int end = start + range.getLength();
        display.setRowData(start, this.players);  
       
    }      

    private void fetchPlayers()
    {        
        //TODO: If games not loaded, wait for event to finish loading games in onGamesChange
        hockeyStatsService.getTeamPlayers(team, this.gamesProvider.getGames().getGamesList(), new AsyncCallback<Set<Player>>() {
            

            public void onFailure(Throwable caught) {
                Window.alert("Remote Procedure Call - Failure" + caught.getMessage());
            }
    
            public void onSuccess(Set<Player> players) {    
                PlayersProvider.this.setPlayers(players);
     
            }
        });     
    }
    
    private void setPlayers(Collection<Player> players)
    {
        this.players = new ArrayList<Player>(players);
        this.playersMap = new TreeMap<Long, Player>();
        for (Player player : this.players)
        {
            this.playersMap.put(player.getPlayerId(), player);
        }
        this.updateRowData(0, new ArrayList<Player>(players));
        this.updateRowCount(players.size(), true); 
       
        firePlayersChangeEvent();
    }

    @Override
    public void onValueChange(ValueChangeEvent<Games> event)
    {
        fetchPlayers();
    }

    public Player getPlayer(Long playerId)
    {
        if (this.playersMap == null)
        {
            return null;
        }
        return this.playersMap.get(playerId);
    }
    public static class PlayersChangeEvent extends ValueChangeEvent<List<Player>>
    {
        protected PlayersChangeEvent(List<Player> players)
        {
            super(players);
        }             
    }  

    
    private void firePlayersChangeEvent()
    {
        this.fireEvent(new PlayersChangeEvent(this.players));
    }     
         

    
    public HandlerRegistration addPlayersChangeHandler(
            ValueChangeHandler<List<Player>> handler)
    {
        return this.handlerManager.addHandler(PlayersChangeEvent.getType(), handler);
    }

    @Override
    public void fireEvent(GwtEvent<?> event)
    {        
        this.eventBus.fireEvent(event);
        this.handlerManager.fireEvent(event);
    } 
}
